import Web3 from 'web3';

// Corrected Mining Contract ABI based on actual contract response
const MINING_CONTRACT_ABI = [
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      }
    ],
    "name": "getUserMiningInfo",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "totalStaked",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "rawPOSPower",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "effectivePOSPower",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "accumulatedReward",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "nftMultiplier",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "activeMinerCount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "lastClaimTimestamp",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      }
    ],
    "name": "getUserMiners",
    "outputs": [
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "minerType",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "miningMode",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "stakedAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "posPower",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "lockStartTime",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "lockEndTime",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "isActive",
            "type": "bool"
          }
        ],
        "internalType": "struct MiningContract.MinerInfo[]",
        "name": "",
        "type": "tuple[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
] as const;

export interface MinerInfo {
  minerType: number;
  miningMode: number;
  stakedAmount: string;
  posPower: string;
  lockStartTime: number;
  lockEndTime: number;
  isActive: boolean;
}

export interface MiningDetails {
  // Summary data from getUserMiningInfo (corrected structure)
  totalStaked: string;
  rawPOSPower: string;
  effectivePOSPower: string;
  accumulatedReward: string;
  nftMultiplier: string;
  activeMinerCount: number;
  lastClaimTimestamp: number;
  
  // Detailed miners from getUserMiners
  miners: MinerInfo[];
  lastUpdateTime: number;
  
  // Computed values
  hasActiveMiners: boolean;
  nftMultiplierPercentage: number;
}

class Web3MiningService {
  private web3: Web3;
  private contract: any;
  private readonly CONTRACT_ADDRESS = '0xD94bF46aF1Bacc4721C1B321785E24c61DBf58D4';
  private readonly RPC_URL = 'https://bsc-dataseed1.binance.org/'; // BSC Mainnet

  constructor() {
    this.web3 = new Web3(this.RPC_URL);
    this.contract = new this.web3.eth.Contract(MINING_CONTRACT_ABI, this.CONTRACT_ADDRESS);
  }

  async getUserMiningInfo(userAddress: string): Promise<MiningDetails> {
    try {
      console.log(`Fetching mining info for address: ${userAddress}`);
      
      // Call both contract functions
      const [summaryResult, minersResult] = await Promise.all([
        this.contract.methods.getUserMiningInfo(userAddress).call(),
        this.contract.methods.getUserMiners(userAddress).call().catch((error: any) => {
          console.warn('getUserMiners call failed, continuing with summary only:', error);
          return [];
        })
      ]);
      
      console.log('Summary result:', summaryResult);
      console.log('Miners result:', minersResult);

      // Process summary data according to actual contract response
      const summary = {
        totalStaked: summaryResult['0']?.toString() || summaryResult.totalStaked?.toString() || '0',
        rawPOSPower: summaryResult['1']?.toString() || summaryResult.rawPOSPower?.toString() || '0',
        effectivePOSPower: summaryResult['2']?.toString() || summaryResult.effectivePOSPower?.toString() || '0',
        accumulatedReward: summaryResult['3']?.toString() || summaryResult.accumulatedReward?.toString() || '0',
        nftMultiplier: summaryResult['4']?.toString() || summaryResult.nftMultiplier?.toString() || '10000',
        activeMinerCount: parseInt(summaryResult['5']?.toString() || summaryResult.activeMinerCount?.toString() || '0'),
        lastClaimTimestamp: parseInt(summaryResult['6']?.toString() || summaryResult.lastClaimTimestamp?.toString() || '0')
      };

      // Process individual miners
      const miners: MinerInfo[] = Array.isArray(minersResult) ? minersResult.map((miner: any) => ({
        minerType: parseInt(miner.minerType?.toString() || '0'),
        miningMode: parseInt(miner.miningMode?.toString() || '0'),
        stakedAmount: miner.stakedAmount?.toString() || '0',
        posPower: miner.posPower?.toString() || '0',
        lockStartTime: parseInt(miner.lockStartTime?.toString() || '0'),
        lockEndTime: parseInt(miner.lockEndTime?.toString() || '0'),
        isActive: Boolean(miner.isActive)
      })) : [];

      // Compute additional values
      const nftMultiplierPercentage = parseFloat(summary.nftMultiplier) / 100; // Convert from basis points to percentage
      const hasActiveMiners = summary.activeMinerCount > 0;

      return {
        ...summary,
        miners,
        hasActiveMiners,
        nftMultiplierPercentage,
        lastUpdateTime: Date.now()
      };

    } catch (error) {
      console.error('Error fetching mining info:', error);
      
      // Provide more detailed error information
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      throw new Error(`Failed to fetch mining info from contract: ${errorMessage}`);
    }
  }

  // Alternative method that only gets summary data (faster)
  async getUserMiningSummary(userAddress: string): Promise<Partial<MiningDetails>> {
    try {
      const result = await this.contract.methods.getUserMiningInfo(userAddress).call();
      
      const nftMultiplier = result['4']?.toString() || result.nftMultiplier?.toString() || '10000';
      const activeMinerCount = parseInt(result['5']?.toString() || result.activeMinerCount?.toString() || '0');
      
      return {
        totalStaked: result['0']?.toString() || result.totalStaked?.toString() || '0',
        rawPOSPower: result['1']?.toString() || result.rawPOSPower?.toString() || '0',
        effectivePOSPower: result['2']?.toString() || result.effectivePOSPower?.toString() || '0',
        accumulatedReward: result['3']?.toString() || result.accumulatedReward?.toString() || '0',
        nftMultiplier,
        activeMinerCount,
        lastClaimTimestamp: parseInt(result['6']?.toString() || result.lastClaimTimestamp?.toString() || '0'),
        hasActiveMiners: activeMinerCount > 0,
        nftMultiplierPercentage: parseFloat(nftMultiplier) / 100,
        lastUpdateTime: Date.now()
      };
    } catch (error) {
      console.error('Error fetching mining summary:', error);
      throw new Error(`Failed to fetch mining summary: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Check if user has active mining (validation helper)
  async hasActiveMining(userAddress: string): Promise<boolean> {
    try {
      const result = await this.contract.methods.getUserMiningInfo(userAddress).call();
      const activeMinerCount = parseInt(result['5']?.toString() || result.activeMinerCount?.toString() || '0');
      return activeMinerCount > 0;
    } catch (error) {
      console.error('Error checking active mining:', error);
      return false;
    }
  }

  // Helper function to format Wei to readable format
  formatWei(weiAmount: string): string {
    try {
      return this.web3.utils.fromWei(weiAmount, 'ether');
    } catch (error) {
      console.error('Error formatting Wei:', error);
      return '0';
    }
  }

  // Helper function to get mining mode name
  getMiningModeName(mode: number): string {
    switch (mode) {
      case 0: return 'Flexible (活期)';
      case 1: return '30 Days';
      case 2: return '90 Days';
      case 3: return '180 Days';
      default: return `Unknown (${mode})`;
    }
  }

  // Helper function to get miner type name
  getMinerTypeName(type: number): string {
    return `M${type}`;
  }

  // Helper function to check if miner is expired
  isMinerExpired(lockEndTime: number): boolean {
    return lockEndTime > 0 && Date.now() > lockEndTime * 1000;
  }

  // Helper function to get remaining lock time
  getRemainingLockTime(lockEndTime: number): string {
    if (lockEndTime === 0) return 'Flexible';
    
    const now = Date.now();
    const endTime = lockEndTime * 1000;
    
    if (now >= endTime) return 'Unlocked';
    
    const diff = endTime - now;
    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
    const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    
    if (days > 0) return `${days} days, ${hours} hours`;
    return `${hours} hours`;
  }

  // Helper to format large numbers
  formatNumber(value: string | number): string {
    const num = typeof value === 'string' ? parseFloat(value) : value;
    if (isNaN(num)) return '0';
    
    if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
    if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
    if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
    
    return num.toFixed(2);
  }

  // Helper to format timestamp to readable date
  formatTimestamp(timestamp: number): string {
    if (timestamp === 0) return 'Never';
    return new Date(timestamp * 1000).toLocaleString();
  }

  // Helper to get NFT multiplier as percentage
  getNFTMultiplierDisplay(multiplier: string): string {
    const percentage = parseFloat(multiplier) / 100;
    return `${percentage.toFixed(2)}%`;
  }

  // Helper to calculate time since last claim
  getTimeSinceLastClaim(lastClaimTimestamp: number): string {
    if (lastClaimTimestamp === 0) return 'Never claimed';
    
    const now = Date.now();
    const lastClaim = lastClaimTimestamp * 1000;
    const diff = now - lastClaim;
    
    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
    const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    
    if (days > 0) return `${days} days, ${hours} hours ago`;
    if (hours > 0) return `${hours} hours ago`;
    
    const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
    return `${minutes} minutes ago`;
  }
}

export const web3MiningService = new Web3MiningService();